/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Collections.Generic;
using System.Text;

namespace ELE.EnterpriseLibrary.Data
{
  /// <summary>
  /// Defines a mapping between an object and one or many relational tables.
  /// </summary>
  public class ObjectSqlMapping : NamedItem, ICloneable
  {
    private string mapName;
    private string objectType;
    private string objectAssembly;
    private string tableName;
    private string queryText;
    private Dictionary<string, ObjectSqlMappingTable> tables;
    private ISqlQueryBuilder queryBuilder;
    /// <summary>
    /// Constructor of an object sql mapping with minimum information
    /// </summary>
    /// <param name="name">The name of the mapping which really refers to the name of the object being mapped</param>
    public ObjectSqlMapping(string name) : base(name) { }
    /// <summary>
    /// Constructor of an object sql mapping with minimum information
    /// </summary>
    /// <param name="name">The name of the mapping which really refers to the name of the object being mapped</param>
    /// <param name="mapName">The actual name of the mapping group</param>
    public ObjectSqlMapping(string name, string mapName) 
      : base(name) 
    {
      this.mapName = mapName;
    }
    /// <summary>
    /// A string that represents the full name of the class that represents an object mapped by this mapping
    /// </summary>
    public string ObjectType
    {
      get { return objectType; }
      set { objectType = value; }
    }
    /// <summary>
    /// The name of the assembly of the object type <see cref="ObjectType"/>
    /// </summary>
    public string ObjectAssembly
    {
      get { return objectAssembly; }
      set { objectAssembly = value; }
    }
    /// <summary>
    /// A mapping must contain a primary table. This property holds the name of that table
    /// </summary>
    public string PrimaryTableName
    {
      get { return tableName; }
      set { tableName = value; }
    }
    /// <summary>
    /// Returns a collection of all tables in this mapping
    /// </summary>
    public ICollection<ObjectSqlMappingTable> Tables
    {
      get 
      {
        if (tables == null)
        {
          return null;
        }
        return tables.Values; 
      }
    }
    /// <summary>
    /// Adds a table to the tables associated with this mapping
    /// </summary>
    /// <param name="table"></param>
    public void AddTable(ObjectSqlMappingTable table)
    {
      if (tables == null)
      {
        tables = new Dictionary<string, ObjectSqlMappingTable>();
      }
      tables.Add(table.Name, table);
    }
    /// <summary>
    /// Gets the primary table of this mapping
    /// </summary>
    /// <returns></returns>
    public ObjectSqlMappingTable GetPrimaryTable()
    {
      return this.GetTable(this.PrimaryTableName);
    }
    /// <summary>
    /// Get a specific table from the mapping by name
    /// </summary>
    /// <param name="name">The name of the table. A null will return the primary table.</param>
    /// <returns></returns>
    public ObjectSqlMappingTable GetTable(string name)
    {
      if (string.IsNullOrEmpty(name))
      {
        name = this.PrimaryTableName;
      }
      return tables[name];
    }
    /// <summary>
    /// The primary key binding of the primary table (read only)
    /// </summary>
    public ObjectPropertySqlBinding PrimaryKeyBinding
    {
      get { return this.GetPrimaryTable().PrimaryKeyBinding; }
    }
    /// <summary>
    /// Get a binding by name.
    /// </summary>
    /// <param name="bindingName"></param>
    /// <returns></returns>
    public ObjectPropertySqlBinding GetBinding(string bindingName)
    {
      foreach (ObjectSqlMappingTable table in this.Tables)
      {
        if(table.ContainsBinding(bindingName))
        {
          return table.GetBinding(bindingName);
        }
      }
      return null;
    }
    /// <summary>
    /// Gets a cloned list of all bingins in the table
    /// </summary>
    /// <returns></returns>
    public List<ObjectPropertySqlBinding> GetBindings()
    {
      List<ObjectPropertySqlBinding> bindings = new List<ObjectPropertySqlBinding>();
      foreach (ObjectSqlMappingTable table in this.Tables)
      {
        foreach (ObjectPropertySqlBinding binding in table.Bindings)
        {
          bindings.Add((ObjectPropertySqlBinding)binding.Clone());
        }
      }
      return bindings;
    }
    /// <summary>
    /// The query builder used by this mapping to generate queries on the fly
    /// </summary>
    public ISqlQueryBuilder QueryBuilder
    {
      get { return queryBuilder; }
      set { queryBuilder = value; }
    }
    /// <summary>
    /// The query text which is built on the fly by using 
    /// either of the methods <see cref="BuildQueryByPropertyBindingText"/> or <see cref="BuildQueryText(MappingQueryType)"/>
    /// </summary>
    public string QueryText
    {
      get { return queryText; }
      set { queryText = value; }
    }
    /// <summary>
    /// Name of the mapping set.
    /// </summary>
    public string MapName
    {
      get { return mapName; }
      set { mapName = value; }
    }
    /// <summary>
    /// Builds up the query text by using a binding as the matching parameters
    /// </summary>
    /// <param name="bindingName"></param>
    public void BuildQueryByPropertyBindingText(string bindingName)
    {
      this.QueryText = this.queryBuilder.BuildSqlQueryString(this, bindingName);
    }
    /// <summary>
    /// Build a query for this mapping based on a type
    /// </summary>
    /// <param name="type"></param>
    public void BuildQueryText(MappingQueryType type)
    {
      this.BuildQueryText(type, this.PrimaryTableName);
    }
    /// <summary>
    /// Build a query for this mapping based on a type and on a specific table
    /// </summary>
    /// <param name="type"></param>
    /// <param name="tableName"></param>
    public void BuildQueryText(MappingQueryType type, string tableName)
    {
      switch(type)
      {
        case MappingQueryType.Select:
          this.QueryText = this.QueryBuilder.BuildSqlQueryString(this);
          break;
        case MappingQueryType.Insert:
          this.QueryText = this.QueryBuilder.BuildSqlInsertString(this, tableName);
          break;
        case MappingQueryType.Update:
          this.QueryText = this.QueryBuilder.BuildSqlUpdateString(this, tableName);
          break;
        case MappingQueryType.Delete:
          this.QueryText = this.QueryBuilder.BuildSqlDeleteString(this, tableName);
          break;
      }
    }

    #region ICloneable Members

    /// <summary>
    /// Implementation of <see cref="ICloneable.Clone()"/>
    /// </summary>
    /// <returns></returns>
    public object Clone()
    {
      ObjectSqlMapping clone = new ObjectSqlMapping(this.Name);
      clone.MapName = this.MapName;
      clone.ObjectAssembly = this.ObjectAssembly;
      clone.ObjectType = this.ObjectType;
      clone.PrimaryTableName = this.PrimaryTableName;
      if (this.Tables != null)
      {
        foreach (ObjectSqlMappingTable table in this.Tables)
        {
          if (!table.Ignore)
          {
            clone.AddTable((ObjectSqlMappingTable)table.Clone());
          }
        }
      }
      clone.QueryBuilder = this.QueryBuilder;
      return clone;
    }

    #endregion
  }
  /// <summary>
  /// Defines a type of query
  /// </summary>
  public enum MappingQueryType
  {
    /// <summary>
    /// Select query
    /// </summary>
    Select, 
    /// <summary>
    /// Insert query
    /// </summary>
    Insert, 
    /// <summary>
    /// Update query
    /// </summary>
    Update, 
    /// <summary>
    /// Delete query
    /// </summary>
    Delete
  }
}
